package com.bluetrum.devicemanager.cmd.request;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.bluetrum.devicemanager.cmd.Request;

import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;

public abstract class TlvRequest extends Request {

    private Map<Byte, byte[]> tlvDataMap;
    protected byte[] preGeneratedPayload;

    public TlvRequest(byte command) {
        super(command);
    }

    /**
     * No need value for some type.
     * @return Empty byte array
     */
    protected byte[] emptyByteArray() {
        return new byte[0];
    }

    protected abstract Map<Byte, Object> getTlvObjectMap();

    public Map<Byte, byte[]> getTlvDataMap() {
        return tlvDataMap;
    }

    /**
     * Use it before {@link #generatePayload()}.
     * @return Pre-generated payload
     */
    protected byte[] getPreGeneratedPayload() {
        return preGeneratedPayload;
    }

    @Nullable
    protected byte[] objectToByteArray(@NonNull Object obj) {
        if (obj instanceof Byte) {
            Byte value = (Byte) obj;
            return new byte[]{ value };
        } else if (obj instanceof Boolean) {
            boolean value = (boolean) obj;
            return new byte[]{ (byte) (value? 1 : 0) };
        } else if (obj instanceof byte[]) {
            return (byte[]) obj;
        }
        return null;
    }

    /**
     * Generate payload data. Invoke it before use {@link #getPreGeneratedPayload()}.
     */
    protected void generatePayload() {
        Map<Byte, Object> entries = getTlvObjectMap();
        // Map<Byte, Object>转成Map<Byte, byte[]>，同时过滤掉null value和不支持情况
        tlvDataMap = entries.entrySet().stream()
                .collect(HashMap::new,
                        (byteArrayMap, byteObjectEntry) -> {
                            Object obj = byteObjectEntry.getValue();
                            // 排除value为null的情况
                            if (obj != null) {
                                byte[] byteArray = objectToByteArray(obj);
                                // 排除不支持，无法转换成byte[]的情况
                                if (byteArray != null) {
                                    byteArrayMap.put(byteObjectEntry.getKey(), byteArray);
                                }
                            }
                        },
                        Map::putAll);
        preGeneratedPayload = generateTlvData(tlvDataMap);
    }

    protected byte[] generateTlvData(Map<Byte, byte[]> entries) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        entries.forEach((key, value) -> {
            os.write(key);
            os.write(value.length);
            os.write(value, 0, value.length);
        });
        return os.toByteArray();
    }
}
